/*
 * Copyright 2012 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.alanjaver.msgpush;

import static com.alanjaver.msgpush.CommonUtilities.SERVER_URL;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;

import org.json.JSONException;
import org.json.JSONObject;

import android.content.Context;
import android.util.Log;

import com.google.android.gcm.GCMRegistrar;

/**
 * Helper class used to communicate with the demo server.
 */
public final class ServerUtilities
{

	private static final int	MAX_ATTEMPTS			= 5;
	private static final int	BACKOFF_MILLI_SECONDS	= 2000;
	private static final Random	random					= new Random();
	
	static boolean register(final Context context, final String regId){
		return register(context, regId, "alan", CommonUtilities.getMD5Str(""));
	}
	
	/**
	 * Register this account/device pair within the server.
	 * 
	 * @return whether the registration succeeded or not.
	 */
	static boolean register(final Context context, final String regId, final String userid, final String password)
	{
		Log.i(CommonUtilities.TAG, "registering device (regId = " + regId + ")");
		String serverUrl = SERVER_URL + "/api.php?action=reg";
		Map<String, String> params = new HashMap<String, String>();
		params.put("gcmid", regId);
		params.put("id", userid);
		params.put("pw", password);
		long backoff = BACKOFF_MILLI_SECONDS + random.nextInt(1000);
		// Once GCM returns a registration id, we need to register it in the
		// demo server. As the server might be down, we will retry it a couple
		// times.
		for (int i = 1; i <= MAX_ATTEMPTS; i++)
		{
			Log.d(CommonUtilities.TAG, "Attempt #" + i + " to register");
			try
			{
				CommonUtilities.displayMessage(context,
						context.getString(R.string.server_registering, i, MAX_ATTEMPTS));
				post(serverUrl, params);
				GCMRegistrar.setRegisteredOnServer(context, true);
				String message = context.getString(R.string.server_registered);
				CommonUtilities.displayMessage(context, message);
				return true;
			}
			catch (IOException e)
			{
				// Here we are simplifying and retrying on any error; in a real
				// application, it should retry only on unrecoverable errors
				// (like HTTP error code 503).
				Log.e(CommonUtilities.TAG, "Failed to register on attempt " + i, e);
				if (i == MAX_ATTEMPTS)
				{
					break;
				}
				try
				{
					Log.d(CommonUtilities.TAG, "Sleeping for " + backoff + " ms before retry");
					Thread.sleep(backoff);
				}
				catch (InterruptedException e1)
				{
					// Activity finished before we complete - exit.
					Log.d(CommonUtilities.TAG, "Thread interrupted: abort remaining retries!");
					Thread.currentThread().interrupt();
					return false;
				}
				// increase backoff exponentially
				backoff *= 2;
			}
		}
		String message = context.getString(R.string.server_register_error, MAX_ATTEMPTS);
		CommonUtilities.displayMessage(context, message);
		return false;
	}

	/**
	 * Unregister this account/device pair within the server.
	 */
	static void unregister(final Context context, final String regId)
	{
		Log.i(CommonUtilities.TAG, "unregistering device (regId = " + regId + ")");
		String serverUrl = SERVER_URL + "/api.php?action=unreg";
		Map<String, String> params = new HashMap<String, String>();
		params.put("gcmId", regId);
		try
		{
			post(serverUrl, params);
			GCMRegistrar.setRegisteredOnServer(context, false);
			String message = context.getString(R.string.server_unregistered);
			CommonUtilities.displayMessage(context, message);
		}
		catch (IOException e)
		{
			// At this point the device is unregistered from GCM, but still
			// registered in the server.
			// We could try to unregister again, but it is not necessary:
			// if the server tries to send a message to the device, it will get
			// a "NotRegistered" error message and should unregister the device.
			String message = context.getString(R.string.server_unregister_error, e.getMessage());
			CommonUtilities.displayMessage(context, message);
		}
	}

	/**
	 * Issue a POST request to the server.
	 * 
	 * @param endpoint
	 *            POST address.
	 * @param params
	 *            request parameters.
	 * 
	 * @throws IOException
	 *             propagated from POST.
	 */
	private static void post(String endpoint, Map<String, String> params) throws IOException
	{
		System.out.println("################################## endpoint : " + endpoint);
		URL url;
		try
		{
			url = new URL(endpoint);
		}
		catch (MalformedURLException e)
		{
			throw new IllegalArgumentException("invalid url: " + endpoint);
		}
		StringBuilder bodyBuilder = new StringBuilder();
		Iterator<Entry<String, String>> iterator = params.entrySet().iterator();
		// constructs the POST body using the parameters
		while (iterator.hasNext())
		{
			Entry<String, String> param = iterator.next();
			bodyBuilder.append(param.getKey()).append('=').append(param.getValue());
			if (iterator.hasNext())
			{
				bodyBuilder.append('&');
			}
		}
		String body = bodyBuilder.toString();
		Log.v(CommonUtilities.TAG, "Posting '" + body + "' to " + url);
		byte[] bytes = body.getBytes();
		HttpURLConnection conn = null;
		try
		{
			// InetSocketAddress addr = new InetSocketAddress("192.168.11.156",
			// 8087);
			// Proxy proxy = new Proxy(Proxy.Type.HTTP, addr); // http 代理
			// conn = (HttpURLConnection) url.openConnection(proxy);

			conn = (HttpURLConnection) url.openConnection();
			conn.setDoOutput(true);
			conn.setUseCaches(false);
			conn.setFixedLengthStreamingMode(bytes.length);
			conn.setRequestMethod("POST");
			conn.setRequestProperty("Content-Type",
					"application/x-www-form-urlencoded;charset=UTF-8");
			// post the request
			OutputStream out = conn.getOutputStream();
			out.write(bytes);
			out.close();
			// handle the response
			int status = conn.getResponseCode();
			if (status != 200)
			{
				throw new IOException("Post failed with error code " + status);
			}
			InputStream in = conn.getInputStream();
			BufferedReader reader = new BufferedReader(new InputStreamReader(in));
			String response = reader.readLine();
			android.util.Log.w(CommonUtilities.TAG, "result code[" + status + "], response string : "	+ response);
			try
			{
				JSONObject json = new JSONObject(response);
				String result = json.getString("result");
				if(!"ok".equals(result)) {
					String cause = json.getString("cause");
					String info = json.getString("info");
					throw new IOException("Server operate failed! cause : "+cause+", info : " + info);
				}
			}
			catch (JSONException e)
			{
				// TODO Auto-generated catch block
				e.printStackTrace();

				throw new IOException("Parse server response string['" + response + "'] failed.");
			}
		}
		finally
		{
			if (conn != null)
			{
				conn.disconnect();
			}
		}
	}
}
